<!--
 * @Descripttion: 课堂分析详情-教师行为-授课音量
 * @version: V1.0.0.1
 * @Author: zhaozhenzhuo
 * @Date: 2025-10-28 14:23:51
 * @LastEditors: zhaozhenzhuo
 * @LastEditTime: 2025-10-28 14:23:51
-->
<template>
	<div class="teaching-volume-wrapper">
		<div class="module-title-wrapper">
			<p>授课音量</p>
			<img v-if="false" src="@/assets/images/lessonAnalysisDetails/text-edit-normal.png" />
		</div>
		<div class="detail">
			<p>
				本堂课来说授课音量大小平均为<span
					v-if="volumeObj?.avgVol || volumeObj?.avgVol === 0"
					class="active"
					>{{ volumeObj.avgVol }}</span
				><span v-else class="active">-</span>，最高<span
					v-if="volumeObj?.maxVol || volumeObj?.maxVol === 0"
					class="active"
					>{{ volumeObj.maxVol }}</span
				><span v-else class="active">-</span>，最低<span
					v-if="volumeObj?.minVol || volumeObj?.minVol === 0"
					class="active"
					>{{ volumeObj.minVol }}</span
				><span v-else class="active">-</span>。
			</p>
		</div>
		<p v-if="volumeChartList && volumeChartList.length" class="introduction">
			（下图展示了本堂课授课音量变化趋势，不代表实际音量分贝值。）
		</p>
		<div v-if="volumeChartList && volumeChartList.length" class="volume-chart"></div>
	</div>
</template>

<script setup lang="ts">
import * as echarts from 'echarts';
import { ref, computed, watch, nextTick, onMounted, shallowRef, onUnmounted } from 'vue';
import type {
	CourseListItemType,
	AnalysisResultType,
	VolumeObjType,
	RealBreakTimeItemType
} from './TeachingVolume.type';
import { getErrorMessage, getAudioVolTaskResultAPI } from '@/api/index';

const props = withDefaults(
	defineProps<{
		queryObj: {
			lessonInfo: CourseListItemType | null;
			aiScoreResult: AnalysisResultType | null;
			offsetTime: number;
			videoBeginTime: number;
		} | null;
		realBreakTimeArr: RealBreakTimeItemType[] | null;
	}>(),
	{
		queryObj: null,
		realBreakTimeArr: null
	}
);

const emit = defineEmits<{
	(e: 'changeVideoTime', time: number): void; // 切换视频时间
}>();

const lessonInfo = computed(() => props.queryObj?.lessonInfo);
const aiScoreResult = computed(() => props.queryObj?.aiScoreResult);
const offsetTime = computed(() => props.queryObj?.offsetTime);

watch(
	() => props.queryObj,
	(val) => {
		if (val && val.aiScoreResult && val.lessonInfo) {
			nextTick(() => {
				getAudioVolTaskResult();
			});
		}
	},
	{ immediate: true }
);

// 窗口大小与1920的比值
const rate = ref(
	(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) / 1920
);

onMounted(() => {
	window.addEventListener('resize', resizeHandler);
});

onUnmounted(() => {
	window.removeEventListener('resize', resizeHandler);

	// 清理 DOM 事件监听器
	if (chartDom.value) {
		if (mousedownHandler.value) {
			chartDom.value.removeEventListener('mousedown', mousedownHandler.value);
			mousedownHandler.value = null;
		}
		if (clickHandler.value) {
			chartDom.value.removeEventListener('click', clickHandler.value);
			clickHandler.value = null;
		}
		chartDom.value = null;
	}

	// 清理 ECharts 实例
	if (volumeChart.value) {
		volumeChart.value.dispose();
		volumeChart.value = null;
	}
});

const resizeHandler = () => {
	setTimeout(() => {
		rate.value =
			(window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) /
			1920;
		initVolumeChart();
	}, 300);
};

// 音量折线图数据
const volumeChartList = ref<number[][]>([]);
// 音量数据
const volumeObj = ref<VolumeObjType | null>(null);

// 查询指定课堂音量分析结果
const getAudioVolTaskResult = async () => {
	try {
		const data = await getAudioVolTaskResultAPI(lessonInfo.value?.code as string);

		// 整理音量数据
		volumeObj.value = {
			avgVol: Math.round(data.total && data.total.avgVol),
			maxVol: Math.round(data.total && data.total.maxVol),
			minVol: Math.round(data.total && data.total.minVol)
		};
		// 整理音量折线图数据
		// 计算采样间距 2h视频 每10s采样一次 一共720个点，取整能被100整除
		const _gap = Math.floor((aiScoreResult.value?.calTotalTime || 0) / 720 / 100) * 100;
		let _index = 0;
		let _startVolume = 0;
		let _endVolume = 0;
		let _firstFlag = true;
		volumeChartList.value = [];
		data.list.forEach((item, index) => {
			// 记录开始和结束的音量
			if (
				item.time < (offsetTime.value || 0) &&
				data.list[index + 1].time > (offsetTime.value || 0)
			) {
				_startVolume = item.db;
			}
			if (
				item.time >= (aiScoreResult.value?.calTotalTime || 0) + (offsetTime.value || 0) &&
				_firstFlag
			) {
				_endVolume = item.db;
				_firstFlag = false;
			}
			// 从课堂开始时取点，课堂结束时停止取点
			if (
				item.time >= (offsetTime.value || 0) + _index * _gap &&
				item.time <= (aiScoreResult.value?.calTotalTime || 0) + (offsetTime.value || 0)
			) {
				volumeChartList.value.push([item.time, item.db]);
				_index += 1;
			}
		});
		// 所有时间向左平移
		volumeChartList.value.forEach((item) => {
			item[0] -= offsetTime.value || 0;
		});
		// 补全第一项和最后一项
		if (volumeChartList.value[0][0] !== 0 && _startVolume) {
			volumeChartList.value.unshift([0, _startVolume]);
		}
		if (
			volumeChartList.value.at(-1)?.[0] !== (aiScoreResult.value?.calTotalTime || 0) &&
			_endVolume
		) {
			volumeChartList.value.push([aiScoreResult.value?.calTotalTime || 0, _endVolume]);
		}
		if (volumeChartList.value && volumeChartList.value.length) {
			nextTick(() => {
				initVolumeChart();
			});
		}
	} catch (err) {
		console.error(getErrorMessage(err));
	}
};

// 音量图当前鼠标所在的时间
const currentVolTime = ref<number>(0);
// echarts对象
const volumeChart = shallowRef<echarts.ECharts | null>(null);
// 保存 chartDom 引用和事件处理函数引用，用于清理
const chartDom = ref<HTMLDivElement | null>(null);
const mousedownHandler = ref<((e: MouseEvent) => void) | null>(null);
const clickHandler = ref<((e: MouseEvent) => void) | null>(null);
// 初始化音量折线图
const initVolumeChart = () => {
	// 清理旧的事件监听器
	if (chartDom.value) {
		if (mousedownHandler.value) {
			chartDom.value.removeEventListener('mousedown', mousedownHandler.value);
			mousedownHandler.value = null;
		}
		if (clickHandler.value) {
			chartDom.value.removeEventListener('click', clickHandler.value);
			clickHandler.value = null;
		}
	}

	// 绘制图像
	if (volumeChart.value) {
		volumeChart.value.dispose();
	}

	// 处理休息时间标记线数据
	const markLineData: Array<{ xAxis: number; label: { formatter: string } }> = [];
	if (props.realBreakTimeArr && props.realBreakTimeArr.length > 0) {
		props.realBreakTimeArr.forEach((item) => {
			// 开始休息的标记线
			if (item.startTime) {
				markLineData.push({
					xAxis: item.startTime - (aiScoreResult.value?.calBeginTime || 0),
					label: {
						formatter: '休息'
					}
				});
			}
			// 结束休息的标记线
			if (item.endTime) {
				markLineData.push({
					xAxis: item.endTime - (aiScoreResult.value?.calBeginTime || 0),
					label: {
						formatter: ''
					}
				});
			}
		});
	}

	// 基于准备好的dom，初始化echarts实例
	volumeChart.value = echarts.init(document.querySelector('.volume-chart') as HTMLElement);
	const option = {
		grid: {
			left: 70 * rate.value,
			right: 35 * rate.value,
			top: 50 * rate.value,
			bottom: props.realBreakTimeArr?.length ? 45 * rate.value : 40 * rate.value
		},
		dataZoom: {
			type: 'inside'
		},
		xAxis: {
			type: 'time',
			name: '时长',
			min: 0,
			max: aiScoreResult.value?.calTotalTime || 0,
			nameLocation: 'start',
			nameTextStyle: {
				padding: [122 * rate.value, 5 * rate.value, 0, 0],
				verticalAlign: 'top',
				fontSize: 12 * rate.value,
				fontWeight: 400,
				fontFamily: 'Alibaba PuHuiTi',
				color: '#666666'
			},
			// minInterval: 15 * 60 * 1000,
			axisLabel: {
				margin: 8 * rate.value,
				fontSize: 12 * rate.value,
				fontWeight: 400,
				fontFamily: 'Alibaba PuHuiTi',
				color: '#666666',
				formatter(value: number) {
					const min = String(Math.floor(value / 60000));
					const sec = String(Math.floor((value - Number(min) * 60000) / 1000));
					return `${min.padStart(2, '0')}:${sec.padStart(2, '0')}`;
				}
			},
			axisTick: {
				length: 3 * rate.value,
				lineStyle: {
					color: '#E3E3E3'
				}
			},
			axisLine: {
				lineStyle: {
					color: '#E3E3E3'
				}
			},
			axisPointer: {
				show: true,
				lineStyle: {
					color: '#0092FF'
				},
				label: {
					show: true,
					lineHeight: 16 * rate.value,
					padding: [3 * rate.value, 5 * rate.value, 1 * rate.value, 5 * rate.value],
					backgroundColor: '#807d8b',
					fontSize: 12 * rate.value,
					fontWeight: 400,
					fontFamily: 'Alibaba PuHuiTi',
					color: '#ffffff',
					formatter: (params: { value: number; seriesData: { value: number[] }[] }) => {
						currentVolTime.value = params.value;
						return `时间：${String(Math.floor(params.value / 1000 / 60)).padStart(2, '0')}:${String(
							Math.floor((params.value / 1000) % 60)
						).padStart(2, '0')}\n音量：${
							params.seriesData &&
							params.seriesData.length &&
							params.seriesData[0] &&
							params.seriesData[0].value &&
							params.seriesData[0].value.length &&
							params.seriesData[0].value[1]
								? params.seriesData[0].value[1].toFixed(0)
								: 0
						}`;
					}
				}
			}
		},
		yAxis: {
			name: '音量',
			nameTextStyle: {
				padding: [0, 30 * rate.value, 0, 0],
				fontSize: 12 * rate.value,
				fontFamily: 'Alibaba PuHuiTi',
				fontWeight: 400,
				color: '#666666'
			},
			axisLabel: {
				fontSize: 12 * rate.value,
				fontWeight: 400,
				fontFamily: 'Alibaba PuHuiTi',
				color: '#666666'
			},
			splitLine: {
				lineStyle: {
					color: ['#EFEFEF']
				}
			},
			minInterval: 20
		},
		series: [
			{
				type: 'line',
				silent: true,
				smooth: true,
				animation: false,
				itemStyle: {
					opacity: 0
				},
				lineStyle: {
					color: {
						type: 'linear',
						x: 0,
						y: 0,
						x2: 0,
						y2: 1,
						colorStops: [
							{
								offset: 0,
								color: '#43F1F7' // 0% 处的颜色
							},
							{
								offset: 1,
								color: '#00A8FF' // 100% 处的颜色
							}
						],
						global: false // 缺省为 false
					}
				},
				areaStyle: {
					origin: 'start',
					color: {
						type: 'linear',
						x: 0,
						y: 0,
						x2: 0,
						y2: 1,
						colorStops: [
							{
								offset: 0,
								color: '#D1E7FD' // 0% 处的颜色
							},
							{
								offset: 1,
								color: '#ffffff' // 100% 处的颜色
							}
						],
						global: false // 缺省为 false
					}
				},
				markLine: {
					silent: true,
					symbol: 'none',
					data: [{ name: '平均值', yAxis: volumeObj.value?.avgVol || 0 }],
					label: {
						formatter: volumeObj.value?.avgVol || 0,
						color: '#00a8ff'
					},
					lineStyle: {
						color: '#00a8ff'
					}
				},
				emphasis: {
					disabled: true
				},
				data: volumeChartList.value
			},
			// 辅助系列用于显示休息时间标记线
			{
				type: 'line',
				name: '休息时间标记',
				silent: true,
				data: [], // 不需要实际数据点
				markLine: {
					silent: false, // 允许触发事件
					symbol: 'none', // 不显示箭头
					label: {
						show: true,
						position: 'start', // 标签显示在线的末端（底部）
						fontSize: 12 * rate.value,
						color: '#22AC38',
						fontFamily: 'Alibaba PuHuiTi',
						fontWeight: 'bold',
						distance: [0, 25 * rate.value] // [水平偏移, 垂直偏移] 增加垂直距离，避免与x轴label重叠
					},
					lineStyle: {
						color: 'rgba(34, 172, 56, 0.3)', // 半透明绿色标记线
						type: 'solid',
						width: 2 * rate.value
					},
					data: markLineData,
					animation: false
				}
			}
		]
	};
	volumeChart.value.setOption(option);

	chartDom.value = document.querySelector('.volume-chart');
	if (chartDom.value) {
		// chart绑定鼠标按下事件
		let _offsetX = 0;
		let _offsetY = 0;

		// 创建并保存 mousedown 事件处理函数
		mousedownHandler.value = (e: MouseEvent) => {
			// 记录一下按下时的坐标
			_offsetX = e.offsetX;
			_offsetY = e.offsetY;
		};

		// 创建并保存 click 事件处理函数
		clickHandler.value = (e: MouseEvent) => {
			// 如果起始点和结束点不在一个位置，就是拖动，不触发点击事件，具体判断条件x、y坐标偏移不超过5px
			if (
				e.offsetX - _offsetX > 5 ||
				e.offsetX - _offsetX < -5 ||
				e.offsetY - _offsetY > 5 ||
				e.offsetY - _offsetY < -5
			) {
				return;
			}
			// 获取像素数组
			const _pixelArr = [e.offsetX, e.offsetY];
			// 将像素数组转换为坐标数组，单位毫秒
			const _convertArr = volumeChart.value?.convertFromPixel({ seriesIndex: 0 }, _pixelArr);
			// 如果点击了图像以外的部分，就不触发下面的事件
			if (
				_convertArr &&
				(_convertArr[0] < 0 || _convertArr[0] > (aiScoreResult.value?.calTotalTime || 0))
			) {
				return;
			}
			// 设置视频时间
			const _currentTime = (currentVolTime.value || 0) / 1000 + (offsetTime.value || 0) / 1000;
			// 通知视频组件弹框
			emit('changeVideoTime', _currentTime);
		};

		// 绑定事件
		chartDom.value.addEventListener('mousedown', mousedownHandler.value);
		chartDom.value.addEventListener('click', clickHandler.value);
	}
};
</script>

<style lang="scss" scoped>
.teaching-volume-wrapper {
	margin-top: 1.6rem;

	.introduction {
		margin-bottom: 0.5rem;
		font-size: $font-size-small-1;
		color: $text-color-secondary;
	}

	.volume-chart {
		height: 20rem;
		background-color: rgba($color: $bg-block-color-primary, $alpha: 0.9);
		text-align: center;
		:deep(canvas) {
			cursor: pointer;
		}
	}
}
</style>
